#include <linux/printk.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/of.h>
#include <asm/io.h>
#include <asm/gpio.h>
#include <linux/irqreturn.h>
#include <linux/interrupt.h>

#include "chip_demo_gpio_button.h"
#include "button_drv.h"


static struct GPIOx *gpio1, *gpio5;
volatile unsigned int *GPIO1_CLK, *GPIO5_CLK;
volatile unsigned int *IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO3;
volatile unsigned int *IOMUXC_SW_MUX_CTL_PAD_UART1_CTS_GPIO1_IO18;
volatile unsigned int *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER0_GPIO5_IO1;

int button_gpio_pins[10];  	//保存引脚
int button_cnts;    		//记录引脚数量
int button_irq;   		//存储中断号
int button_gpio = 18;  	//按键引脚在GPIO子系统中的编号

static irqreturn_t button_handle(int irqs, void *dev_id)
{
	int value;
	/*读取 button 的值*/
	value = gpio_get_value(button_gpio);
	printk("read button = %d, %s\n", value, value == 1 ? "button up" : "button on");
	return IRQ_HANDLED;
}


/*
	接收 button_drv.c 传递的初始化引脚参数（接收button_drv.c传递的次设备号）
	负责初始化GPIO
*/
static int button_chip_init(int which)
{
	int gro, p;
	int err;
	
	gro = GROUP(button_gpio_pins[which]);   //所属GPIO组
	p = pin(button_gpio_pins[which]);		 //第几个引脚
	
	printk("%s: control button of %d\n", __FUNCTION__, which);
	printk("init group %d, pin %d\n", gro, p);

	/* 初始化具体GPIO */
	switch(gro){
		case 1:
		{
			switch(p){
				/*	led : GPIO1_IO3 
				   	配置为GPIO输出*/
				case 3:{  
					if(!IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO3){
						/* 映射CLK寄存器 */
						GPIO1_CLK = ioremap(CCM_CCGR1, 4);
						
						/* 映射GPIOx寄存器，包含数据、输出方向寄存器 */
						gpio1 = ioremap(GPIO1_DR_ADDR, sizeof(struct GPIOx));

						/* 使能时钟 */
						*GPIO1_CLK |= (3 << 26);

						/* 设置GPIO方向：输出 */
						gpio1->GDIR |= (1 << p);

						/* 映射IOMUXC寄存器，设置成GPIO外设 */
						IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO3 = ioremap(IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO00_ADDR+0X04*p,4);

						/* 设置成GPIO外设 */
						*IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO3 &= 0x0f;
						*IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO3 |= 0x5;
					}
					break;
				}
				/* 	key : GPIO1_IO18 
				   	配置为下降沿触发中断*/
				case 18:{
					if(!IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO3){
						/* 映射CLK寄存器 */
						GPIO1_CLK = ioremap(CCM_CCGR1, 4);
						
						/* 映射GPIOx寄存器，包含数据、输出方向寄存器 */
						gpio1 = ioremap(GPIO1_DR_ADDR, sizeof(struct GPIOx));

						/* 使能时钟 */
						*GPIO1_CLK |= (3 << 26);
						
						/************* 4.button引脚申请中断 ************/
						button_irq = gpio_to_irq(button_gpio);
						printk("request irq = %d\n", button_irq);
						/*下降沿沿触发 */
						err = request_irq(button_irq, button_handle, IRQF_TRIGGER_FALLING, 
										  "button_irq", NULL);  // 
						if(err != 0){
							printk("request irq error!\n");
						}
					
					}
					break;
				}
			}
		}
			
		case 5:
		{
			switch(p)
			{
				/* beep : GPIO5_IO1 */
				case 1:{
					if(!IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER0_GPIO5_IO1){
						/* 映射CLK寄存器 */
						GPIO5_CLK = ioremap(CCM_CCGR1, 4);
						
						/* 映射GPIOx寄存器，包含数据、输出方向寄存器 */
						gpio5 = ioremap(GPIO5_DR_ADDR, sizeof(struct GPIOx));
						
						/* 映射IOMUXC寄存器，设置成GPIO外设 */
						IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER0_GPIO5_IO1 = ioremap(IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER0_ADDR+0x04*p,4);

						/* 设置成GPIO外设 */
						*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER0_GPIO5_IO1 &= 0x0f;
						*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER0_GPIO5_IO1 |= 0x5;
						
						/* 使能时钟 */
						*GPIO5_CLK |= (3 << 30);

						/* 设置GPIO方向：输出 */
						gpio5->GDIR |= (1 << p);
					}
					break;
				}
				default:
					break;
			}
			
		}
		default:
			break;
	}
	
	return 0;
}


/*
	接收 button_drv.c 传递的控制引脚参数
	负责控制GPIO输出高低电平
*/
static int button_chip_ctl(int which, char status)
{
	int gro, p;
	
	gro = GROUP(button_gpio_pins[which]);   //所属GPIO组
	p = pin(button_gpio_pins[which]);		 //第几个引脚
	
	printk("%s: control button of %d, status = %d\n", __FUNCTION__, which, status);
	printk("control group %d, pin %d\n", gro, p);

	/* 控制具体GPIO */
	switch(gro){
		case 1:
			gpio1 = ioremap(GPIO1_DR_ADDR, sizeof(struct GPIOx));
			if(status)
				gpio1->DR |= (1<<p);
			else
				gpio1->DR &= ~(1<<p);
			break;
			
		case 2:
			break;
			
		case 3:
			break;
			
		case 4:
			break;
			
		case 5:
			gpio5 = ioremap(GPIO5_DR_ADDR, sizeof(struct GPIOx));
			if(status)
				gpio5->DR |= (1<<p);
			else
				gpio5->DR &= ~(1<<p);
			break;
			
		default:
			break;
	}
	
	return 0;
}

	
/*
	button_chip_operations 结构体变量
	GPIO的初始化和控制函数指针
*/
static struct button_chip_operations button_chip_ops = {
	.init = button_chip_init,
	.ctl  = button_chip_ctl,
};

static int chip_button_probe(struct platform_device *dev)
{
	/* 从设备树节点中提取出引脚 */
	struct device_node *np = dev->dev.of_node;
	int button_pin, err;

	/* 定义二级指针 */
	char name[20]={0};
	const char *str_name;
	str_name = name;

	/* 此platform_driver支持的platform_device可能不来自设备树
	这里要判断一下*/
	if(!np)
		return -1;

	/* 从设备树节点中取出 my_name 属性 */
	err = of_property_read_string(np, "my_name", &str_name);

	/* 从设备树节点中取出pin属性 */
	err = of_property_read_u32(np, "pin", &button_pin);

	printk("chip_button_probe fun, name=%s, group=%d, pin=%d\n", str_name,GROUP(button_pin),pin(button_pin));

	button_gpio_pins[button_cnts] = button_pin;
	/* 创建设备节点 */
	button_device_create(button_cnts, (char*)str_name);
	button_cnts++;
	
	return 0;
}

static int chip_button_remove(struct platform_device *dev)
{
	int i;
	int button_pin, err;
	int flag = 0;  //判断是否所有设备节点都被销毁

	/* 定义二级指针 */
	char name[20]={0};
	const char *str_name;
	
	/* 从设备树节点中提取出引脚 */
	struct device_node *np = dev->dev.of_node;
	/* 此platform_driver支持的platform_device可能不来自设备树
	这里要判断一下*/
	if(!np)
		return -1;
	/* 从设备树节点中取出 pin 属性 */
	err = of_property_read_u32(np, "pin", &button_pin);

	/* 从设备树节点中取出 my_name 属性 */
	str_name = name;
	err = of_property_read_string(np, "my_name", &str_name);
	
	printk("chip_button_remove fun, name=%s, group=%d, pin=%d\n", str_name,GROUP(button_pin),pin(button_pin));
	
	/* 卸载设备节点 */
	for(i=0; i<button_cnts; i++)
	{
		if(button_gpio_pins[i] != -1)
			flag = 1;
		if(button_gpio_pins[i] == button_pin)
		{
			button_device_destroy(i);
			button_gpio_pins[i] = -1;
		}
	}
	if(flag == 0)
		button_cnts = 0;
	
	return 0;
}


static const struct of_device_id button_match[] = {
	{ .compatible = "hc-led-beep" },
	{ .compatible = "hc-led-beep" },
	{ .compatible = "hc-key" },
	{},
};


static struct platform_driver chip_button_platform_drv = {
	.probe		= chip_button_probe,
	.remove		= chip_button_remove,
	.driver		= {
		.name	= "led-beep-key",
		.of_match_table = button_match,
	},
};

static int chip_button_platform_init(void)
{
	int err;
	err = platform_driver_register(&chip_button_platform_drv);
	get_button_resources(&button_chip_ops);

	return 0;
}

static void chip_button_platform_exit(void)
{
	platform_driver_unregister(&chip_button_platform_drv);
}

module_init(chip_button_platform_init);
module_exit(chip_button_platform_exit);
MODULE_LICENSE("GPL");



